# -*- coding: utf-8 -*-

import time
import sha, os, os.path

from amplee.error import *
from amplee.atompub.member import MemberResource
from amplee.http_helper import get_best_mimetype
from amplee.utils import get_isodate, generate_uuid_uri, \
     compute_etag_from_feed, compute_etag_from_entry, \
     decode_slug, handle_if_match, safe_unquote, \
     extract_media_type, extract_url_trail


__all__ = ['AtomPub']


class AtomPub(object):
    
    def __init__(self, collection):
        self.c = collection

    def GET(self, environ, start_response):
        if not environ['selector.vars'].has_key('id'):
            feed = self.c.feed_handler.retrieve()
            start_response("200 OK", [('Content-type', 'application/atom+xml;type=feed'),('ETag',compute_etag_from_feed(feed))])
            return [feed.xml()]

        id = safe_unquote(environ['selector.vars']['id']+('.'+environ['selector.vars']['format'] if environ['selector.vars']['format'] != None else ''))

	member_id, media_id = self.c.convert_id(id)

        member = self.c.get_member(member_id)
        if not member:
            start_response("404 Not found", [('Content-type', 'text/html')])
            return ["Not found"]

	response_headers = []
        if id.endswith('.atom'):
            content = member.xml(indent=True)
	    response_headers.append(('Content-type', 'application/atom+xml;type=entry'))
        else:
            content = member.content
            member.media_type = extract_media_type(member.atom)
            response_headers.append(('Content-Type',member.media_type.encode('utf-8')))
            
	response_headers.append(('ETag',compute_etag_from_entry(member.atom).encode('utf-8')))        
	start_response("200 OK", response_headers)
        return [content]
    
    def HEAD(self, environ, start_response):

	if not environ['selector.vars'].has_key('id'):
            feed = self.c.feed_handler.retrieve()
            start_response("200 Ok", [('Content-Length', str(len(feed.xml()))),('ETag',compute_etag_from_feed(feed))])
            return ['']

        id = safe_unquote(environ['selector.vars']['id']+'.'+environ['selector.vars']['format'])

	member_id, media_id = self.c.convert_id(id)

        member = self.c.get_member(member_id)
        if not member:
            start_response("404 Not found", [('Content-type', 'text/html')])
            return ["Not found"]

        if id.endswith('.atom'):
            content = member.xml(indent=True)
        else:
            content = member.content
            member.media_type = extract_media_type(member.atom)
    	
	length = str(len(content))
	
	start_response("200 Ok",[('Content-Length',length),('ETag',compute_etag_from_entry(member.atom).encode('utf-8'))])
        return ['']

    def POST(self, environ, start_response):
        if self.c.is_read_only:
	    start_response("400", [('Content-type', 'text/html')])
            return ['Creation of resources not accepted to this collection']
        
        
        if not environ.has_key('CONTENT_TYPE') or environ['CONTENT_TYPE'] == "":
	    start_response("400", [('Content-type', 'text/html')])
            return ['Missing content type']
	
        if not environ.has_key('CONTENT_LENGTH') or environ['CONTENT_LENGTH'] == "":
	    start_response("411", [('Content-type', 'text/html')])
            return ['Length Required']

        length = int(environ['CONTENT_LENGTH'])

        mimetype = get_best_mimetype(environ['CONTENT_TYPE'], self.c.accept_media_types,
                                     check_params=False, return_full=True)
        if not mimetype:
            start_response("415 Bobo", [('Content-type', 'text/html')])
            return ['Unsupported Media Type']

        # That is a specific case where the mimetype also contains
        # the multipart boundary that we want to strip here
        if mimetype.startswith('multipart/form-data'):
            mimetype = 'multipart/form-data'

        # Generates the actual atom member entry (or the MLE)
	if environ.has_key('SLUG') and environ['SLUG'] != "":
	    slug = decode_slug(environ['SLUG'])
        else:
            slug = None
	
	member = MemberResource(self.c, media_type=mimetype)
	
        content = member.generate(mimetype, source=environ['wsgi.input'], slug=slug,length=length, preserve_dates=False)
        
        message = 'Adding %s' % member.member_id
        if member.is_entry_mimetype(mimetype):
            self.c.attach(member, member_content=member.atom.xml())
        else:
            self.c.attach(member, member_content=member.atom.xml(),
                          media_content=environ['wsgi.input'].read(length))
        self.c.store.commit(message=message)

        # This bloc will generate the public version of the
        # member atom resource


	# Regenerate the collection feed
        self.c.feed_handler.set(self.c.feed)
	response_headers = []        
	member_uri = member.member_uri
        if member_uri:
            response_headers.append(('Location', member_uri.encode('utf-8')))
            response_headers.append(('Content-Location', member_uri.encode('utf-8')))
            
        response_headers.append(('Content-Type',mimetype.encode('utf-8')))
        response_headers.append(('ETag', compute_etag_from_entry(member.atom)))
	
	start_response('201 Created', response_headers)
        return [member.xml()]

    def PUT(self, environ, start_response):
        if not environ.has_key('CONTENT_TYPE') or environ['CONTENT_TYPE'] == "":
	    start_response("400", [('Content-type', 'text/html')])
            return ['Missing content type']

        mimetype = get_best_mimetype(environ['CONTENT_TYPE'], self.c.editable_media_types,
                                     check_params=False, return_full=True)
        if not mimetype:
            start_response("415 Bobo", [('Content-type', 'text/html')])
            return ['Unsupported Media Type']

        # The problem with this media-type is that it also contains
        # the multipart boundary value within the media-type
        # and that value is per client so here we do force
        # the media-type to be stripped of its parameters
        if mimetype.startswith('multipart/form-data'):
            mimetype = 'multipart/form-data'

        if not environ.has_key('CONTENT_LENGTH') or environ['CONTENT_LENGTH'] == "":
	    start_response("411", [('Content-type', 'text/html')])
            return ['Length Required']

        length = int(environ['CONTENT_LENGTH'])
        
	id = safe_unquote(environ['selector.vars']['id']+('.'+environ['selector.vars']['format'] if environ['selector.vars']['format'] != None else ''))
        member_id, media_id = self.c.convert_id(id)
        member = self.c.get_member(member_id)

        if not member:
            start_response("404 Not found", [('Content-type', 'text/html')])
            return ["Not found"]

        if environ.has_key('IF-MATCH'):
            try:
                handle_if_match(compute_etag_from_entry(member.atom), environ['IF-MATCH'] or [])
            except ResourceOperationException, roe:
		start_response(roe.code, [('Content-type', 'text/html')])
            	return [roe.msg]

        new_member = MemberResource(self.c, media_type=mimetype)
        content = new_member.generate(mimetype, source=environ['wsgi.input'], existing_member=member,length=length)
        new_member.update_dates()
        

        message = 'Updating %s' % new_member.member_id
        if new_member.is_entry_mimetype(mimetype):
            self.c.attach(new_member, member_content=new_member.atom.xml())
        else:
            self.c.attach(new_member, member_content=new_member.atom.xml(),
                          media_content=environ['wsgi.input'].read(length))
        self.c.store.commit(message=message)

	
        # Regenerate the collection feed
        self.c.feed_handler.set(self.c.feed)

        start_response("200 Ok", [('Content-type', mimetype.encode('utf-8')),('ETag', compute_etag_from_entry(new_member.atom))])
        return [new_member.xml()]

    def DELETE(self, environ, start_response):
        id = safe_unquote(environ['selector.vars']['id']+'.'+environ['selector.vars']['format'])
        member_id, media_id = self.c.convert_id(id)
        member = self.c.get_member(member_id)
        
	if not member:
            start_response("404 Not found", [('Content-type', 'text/html')])
            return ["Not found"]


        self.c.prune(member_id, media_id)
        self.c.store.commit(message="Deleting %s and %s" % (member_id, media_id))
        self.c.feed_handler.remove(member.atom)
        
        # Regenerate the collection feed
        self.c.feed_handler.set(self.c.feed)
	start_response("200 Ok", [])
	return [""]

